﻿using System;
using System.Collections.Generic;
using System.Xml;
using System.Linq;
using System.Xml.Linq;
using System.Web;
using System.Reflection;
using System.IO;

namespace Com.FastSearch.SharePoint.BL.SearchManager
{
    /// <summary>
    /// Helper to provide Mime type images and map some fields from the ESP index profile to some
    /// predefined microsoft Result Object fields.
    /// 
    /// The image mapping uses some predefined settings from an embadded mapping file.
    /// In addition the mapping can be everriden by placing a image mapping file (ImageToMimeMapping.xml) into the bin folder of
    /// the web application.
    /// The ImageToMimeMapping.xml file looks like:
    /// <code>
    /// &lt;?xml version="1.0" encoding="utf-8" ?&gt;
    /// &lt;ImageMappings&gt;
    ///   &lt;ImageMapping MimeType="text/html" Image="/_layouts/images/STM16.GIF"/&gt;
    ///   &lt;ImageMapping MimeType="text/plain" Image="/_layouts/images/STS_Web17.gif"/&gt;  
    /// &lt;/ImageMappings&gt;
    /// </code>
    /// 
    /// </summary>
    class ESPToMSSearchHelper
    {
        #region Private variables

        /// <summary>
        /// Default image which is used of no mapping for a mime type has been defined.
        /// It's part of a MOSS 2007 installation
        /// </summary>
        private static readonly string DEFAULT_IMAGE_URL = "/_layouts/images/STM16.GIF";

        /// <summary>
        /// Mapping file name of the embadded and custom config file for the mime type to image mapping.
        /// </summary>
        private static readonly string IMAGE_MIME_MAPPING_FILE = "ImageToMimeMapping.xml";

        /// <summary>
        /// Dictionary containing the mappings from the ESP index profile to the MS Search Object fields
        /// </summary>
        public Dictionary<string, string> ESPtoMSMapping = new Dictionary<string, string>();

        /// <summary>
        /// Dictionary containing the mime type to image mappings
        /// </summary>
        public Dictionary<string, string> ESPtoMSImageMapping = new Dictionary<string, string>();

        /// <summary>
        /// Instance of the ESPToMSSearchHelper
        /// </summary>
        private static ESPToMSSearchHelper instance = null;

        /// <summary>
        /// last changed time of the currently loaded custom mime type mapping file.
        /// Used to reload the mapping if it changed since it was read the last time.
        /// </summary>
        private static DateTime configFileDate;
        #endregion


        // Singelton access
        private ESPToMSSearchHelper()
        {
        }

        /// <summary>
        /// Return an instance of the ESPToMSSearchHelper.
        /// Creates a new instance if the Custom Mime Type mapping file has changed
        /// </summary>
        public static ESPToMSSearchHelper Instance
        {
            get
            {
                if (instance == null || instance.DidMapingCustomFileChange())
                {
                    instance = new ESPToMSSearchHelper();
                    instance.BuildESPtoMSMappingDictionary();
                    instance.BuildESPtoMSImageMappingDictionary();         
                }
                return instance;
            }
        }

        /// <summary>
        /// Check if the Custom Mime Type to Image mapping file (ImageToMimeMapping.xml) changed.
        /// </summary>
        /// <returns>true if the file changed, false otherwise</returns>
        public bool DidMapingCustomFileChange() {
            try
            {
                string file = instance.CustomMimeTypeMappingConfigFile;
                if (!File.Exists(file))
                {
                    // IF the configFileDate is != new DateTime it meas that there was 
                    // a custom file there but it has been removed now.
                    if (configFileDate.CompareTo(new DateTime()) != 0)
                    {
                        return true;
                    }

                    return false;
                }
                DateTime lastChange = File.GetLastWriteTime(file);
                if (lastChange.CompareTo(configFileDate) != 0)
                {
                    return true;
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message + " " + ex.StackTrace);
            }
            return false;
        }

        /// <summary>
        /// Load an embadded xslt file from the Com.FastSearch.SharePoint.BL.SearchManager.dll within the config folder.
        /// </summary>
        /// <param name="xsl">the name of the embadded xslt file within the xslt project folder</param>
        /// <returns>the xslt file content as string</returns>
        protected static string LoadXslFromAssembly(string xsl)
        {
            Assembly assembly = Assembly.GetExecutingAssembly();
            // Get the Xsl out of the assembly - Mark Xsl as an Embedded resource for this to work.
            Stream stream = assembly.GetManifestResourceStream("Com.FastSearch.SharePoint.BL.SearchManager.config." + xsl);
            StreamReader r = new StreamReader(stream);
            return r.ReadToEnd();
        }       

        /// <summary>
        /// Build up the ESP index profile to MS Result Object fields mapping.
        /// Only a few default fields are mapped, which are used by the default 
        /// xslt for result list display.
        /// <remarks>
        /// This fields are hard coded for now as all fields as returned from ESP are directly accessible as well</remarks>
        /// </summary>
        private void BuildESPtoMSMappingDictionary()
        {
            // TODO: Make configurable??
            ESPtoMSMapping.Add("body", "description");
            ESPtoMSMapping.Add("docdatetime", "write");
            ESPtoMSMapping.Add("internalid", "id");
            ESPtoMSMapping.Add("collection", "site"); // Is a collection used the same as the site in the connector
            ESPtoMSMapping.Add("webauthor", "author");
        }

        /// <summary>
        /// Get the file location of the ImageToMimeMapping.xml file within the web application.
        /// </summary>
        private string CustomMimeTypeMappingConfigFile
        {
            get
            {
                string file = System.Web.HttpContext.Current.Server.MapPath(@"\bin\") + IMAGE_MIME_MAPPING_FILE;                
                return file;
            }
        }

        /// <summary>
        /// First load the embadded image mapping file and then try to also load the custom image mapping file 
        /// which has to be located in the webapplicationroot/bin directory
        /// </summary>
        private void BuildESPtoMSImageMappingDictionary()
        {
            try
            {
                string mimetypexml = LoadXslFromAssembly(IMAGE_MIME_MAPPING_FILE);
                BuildESPtoMSImageMappingDictionary(mimetypexml);
                
            }
            catch (Exception)
            {}

            // Now try to load the custom mime types
            try
            {                
                string file = CustomMimeTypeMappingConfigFile;
                System.IO.StreamReader streamReader = new System.IO.StreamReader(file);
                string mimetypexml = streamReader.ReadToEnd();
                streamReader.Close();
                BuildESPtoMSImageMappingDictionary(mimetypexml);
                configFileDate = File.GetLastWriteTime(file);
            }
            catch (Exception)
            {               
            }
        }

        /// <summary>
        /// build up the mime type to image mapping dictionary from an xml string
        /// </summary>
        /// <param name="mimetypexml">xml string as defined for the ImageToMimeMapping.xml file</param>
        private void BuildESPtoMSImageMappingDictionary(string mimetypexml)
        {
                           
                XElement mimexml = XElement.Parse(mimetypexml);
                var resultlist = from result in mimexml.Descendants("ImageMapping")
                                 where result != null
                                 select new
                             {
                                 MIME = result.Attribute("MimeType").Value,
                                 IMAGE = result.Attribute("Image").Value,
                             };

                foreach (var imagemapping in resultlist)
                {
                    if (!string.IsNullOrEmpty(imagemapping.MIME) && !string.IsNullOrEmpty(imagemapping.IMAGE))
                    {
                        if (ESPtoMSImageMapping.ContainsKey(imagemapping.MIME))
                        {
                            ESPtoMSImageMapping.Remove(imagemapping.MIME);
                        }
                        ESPtoMSImageMapping.Add(imagemapping.MIME, imagemapping.IMAGE);
                    }
                }         
        }

        /// <summary>
        /// Return the Image for a mime type
        /// </summary>
        /// <param name="mimetype">mime type string</param>
        /// <returns>Image as defined in the ImageToMimeMapping.xml file(s). If not found a default image is returned.</returns>
        public string GetImageUrlForContent(string mimetype)
        {
            if (String.IsNullOrEmpty(mimetype))
            {
                return DEFAULT_IMAGE_URL;
            }

            return !ESPtoMSImageMapping.ContainsKey(mimetype) ? DEFAULT_IMAGE_URL : ESPtoMSImageMapping[mimetype];
        }
    }
}